package xerror

import (
	"errors"
	"fmt"
	"reflect"
)

type ErrorCode uint64

const (
	CodeSuccess          ErrorCode = 0
	CodeInvalidArgument  ErrorCode = 40001
	CodePermissionDenied ErrorCode = 40002
	// CodeInternalServer 服务内部异常,该异常会打印详细日志
	CodeInternalServer ErrorCode = 50000
	// CodeRecoverServer 重启时的错误码,重启时会打印gin的堆栈日志, 不用CodeInternalServer日志
	CodeRecoverServer ErrorCode = 50001
	// CodeTooManyRequests 请求过于频繁
	CodeTooManyRequests ErrorCode = 50002
)

var (
	ErrParameterError    = NewArgument("参数错误")
	ErrInvalidArgument   = NewArgument("invalid argument")
	ErrNilUpdateArgument = NewArgument("没有数据需要更新 请检查后重试")
	ErrNilList           = NewArgument("未查询到数据")
	ErrPermissionDenied  = NewArgument("permission denied")
	ErrRecover           = New(fmt.Errorf("服务异常 请稍后重试或联系客服处理"), CodeRecoverServer, nil)
	ErrTooManyRequests   = NewArgument("too many requests")
	ErrNilAccount        = NewArgument("获取用户信息失败, 请尝试重新登录")
	ErrDelIsDeleted      = NewArgument("数据已删除, 请勿重复操作")
)

type Error struct {
	Err  error
	Code ErrorCode
	Meta interface{}
}

var _ error = (*Error)(nil)

func (msg *Error) IsInternal() bool {
	return msg.Code == CodeInternalServer
}

func (msg *Error) SetCode(code ErrorCode) *Error {
	msg.Code = code
	return msg
}

// implements the error interface.
func (msg *Error) Error() string {
	return msg.Err.Error()
}

func (msg *Error) Unwrap() error {
	return msg.Err
}

func (msg *Error) SetMeta(data interface{}) *Error {
	msg.Meta = data
	return msg
}

func (msg *Error) JSON() interface{} {
	jsonData := make(map[string]interface{})
	if msg.Meta != nil {
		value := reflect.ValueOf(msg.Meta)
		switch value.Kind() {
		case reflect.Struct:
			return msg.Meta
		case reflect.Map:
			for _, key := range value.MapKeys() {
				jsonData[key.String()] = value.MapIndex(key).Interface()
			}
		default:
			jsonData["meta"] = msg.Meta
		}
	}
	if _, ok := jsonData["error"]; !ok {
		jsonData["error"] = msg.Error()
	}
	return jsonData
}

func New(err error, c ErrorCode, meta interface{}) error {
	if err == nil {
		return nil
	}
	if e, ok := err.(*Error); ok {
		e.Code = c
		e.Meta = meta
		return e
	}
	return &Error{
		Err:  err,
		Code: c,
		Meta: meta,
	}
}

func NewArgumente(err error) error {
	return New(err, CodeInvalidArgument, nil)
}

func NewArgument(err string) error {
	return New(errors.New(err), CodeInvalidArgument, nil)
}

func Newf(c ErrorCode, meta interface{}, format string, v ...interface{}) error {
	return New(fmt.Errorf(format, v...), c, meta)
}

func NewArgumentf(format string, v ...interface{}) error {
	return New(fmt.Errorf(format, v...), CodeInvalidArgument, nil)
}

// ToError 验证是否可转化为 *Error
func ToError(err any) (*Error, bool) {
	res, ok := toError(err)
	if ok {
		return res, ok
	}

	e, ok := err.(error)
	if !ok {
		return nil, false
	}
	res, ok = toError(errors.Unwrap(e))
	if !ok {
		return nil, false
	}
	return &Error{
		Err:  e,
		Code: res.Code,
	}, true
}

func toError(err any) (*Error, bool) {
	res, ok := err.(*Error)
	return res, ok
}
